package com.analysis.service.impl;

import java.math.BigDecimal;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;

import com.analysis.common.CalculationUtil;
import com.analysis.common.ConfigUtil;
import com.analysis.common.DateUtil;
import com.analysis.model.KLine;
import com.analysis.model.OLS;
import com.analysis.model.Similarity;
import com.analysis.service.OLSService;

@Service
public class OLSServiceImpl implements OLSService {

    KLineServiceImpl kline = new KLineServiceImpl();
    DataSetServiceImpl dataSet = new DataSetServiceImpl();

    @Override
    public List<Similarity> olsAnalyse(String setName, String symbol, Date startDate, Date endDate,int scale) {
        List<OLS> osls = new ArrayList<>();

        List<KLine> targetKline = kline.getSubKline(symbol, startDate, endDate);
        List<Double> targets = toDouble(targetKline);
        int num = targets.size();

        List<String> set;
        if ("true".equalsIgnoreCase(ConfigUtil.getString("specificSet")) && setName != null) {
            set = dataSet.getSpecificDataSet(setName);
        } else {
            set = dataSet.getRandomDataSet();
        }

        for(String eachSymbol:set){
            List<KLine> eachKline = kline.getKline(eachSymbol);
            String name = kline.getName(eachSymbol);
            System.out.println(" Calculate " + eachSymbol);
            
            List<OLS> eachOsls = new ArrayList<>();
            for (int i = 0; i + num < eachKline.size() + 1; i += scale) {
                List<KLine> subKline = eachKline.subList(i, i + num);
                KLine first = eachKline.get(i);
                KLine last = eachKline.get(i + num - 1);
                List<Double> eachDouble = toDouble(subKline);
                OLS ols = calculatOLS(targets, eachDouble);
                ols.setXkline(subKline);
                ols.setyKline(targetKline);
                ols.setName(name);
                ols.setSymbol(eachSymbol);
                eachOsls.add(ols);
            }
            osls.addAll(eachOsls);
        }

        //除去相关系数小于0的，并且进行排序，取前20
        return osls.stream()
        		.filter(f -> f.getRxy() > 0)
                .sorted(Comparator.comparing(OLS::getR_squared).reversed())
                .limit(20).map(ols -> {
            Similarity simi = new Similarity();
            simi.setKline(ols.getXkline());
            simi.setName(ols.getName());
            simi.setSymbol(ols.getSymbol());
            simi.setLines(kline.getLine(simi.getKline()));
            simi.setSimilarity(new BigDecimal(CalculationUtil.toSpecifiedDecimals(ols.getR_squared(), 5) + ""));
                    simi.setCoefficient(new BigDecimal(CalculationUtil.toSpecifiedDecimals(ols.getRxy(), 5) + ""));

            return simi;
        }).collect(Collectors.toList());
    }

    @Override
    public OLS calculatOLS(List<Double> targets, List<Double> sample) {
        OLS ols = new OLS();
        double xAvg = CalculationUtil.calAverages(sample);// tagets:y,sample:x
        double yAvg = CalculationUtil.calAverages(targets);

        double SxySqu = 0;
        double SxSqu = 0;
        double beta0 = 0;
        double beta1 = 0;
        double SST = 0;
        double SSE = 0;
        double SSR = 0;
        double Rxy = 0;

        for (int i = 0; i < targets.size(); i++) {
            Double x = sample.get(i);
            Double y = targets.get(i);

            SxySqu += (x - xAvg) * (y - yAvg);
            SxSqu += Math.pow(x - xAvg, 2);
            SST += Math.pow(y - yAvg, 2);
        }

        Rxy = SxySqu / ((Math.pow(SxSqu, 0.5)) * (Math.pow(SST, 0.5)));
        beta1 = SxySqu / SxSqu;
        beta0 = yAvg - beta1 * xAvg;

        for (int i = 0; i < targets.size(); i++) {
            Double x = sample.get(i);
            Double y = targets.get(i);

            SSE += Math.pow((beta0 + x * beta1 - yAvg), 2);
            SSR += Math.pow((y - beta0 - x * beta1), 2);
        }

        ols.setBeta0(beta0);
        ols.setBeta1(beta1);
        ols.setSSE(SSE);
        ols.setSSR(SSR);
        ols.setSST(SST);
        ols.setR_squared(SSE / SST);
        ols.setRxy(Rxy);
        return ols;
    }

    public List<Double> toDouble(List<KLine> klines) {
        return klines.stream().map(each -> {
            return each.getClose();
        }).collect(Collectors.toList());
    }

    public static void main(String[] args) {
        new OLSServiceImpl()
                .olsAnalyse(null, "sh600674", DateUtil.getSqlDate("2009-03-04"),
                DateUtil.getSqlDate("2009-07-04"),10).forEach(si -> {
                    List<KLine> sub = si.getKline();
                    System.out.println("股票代码：" + si.getSymbol() + si.getName() + "相似度：" + si.getSimilarity() + "开始时间："
                            + sub.get(0).getDay() + "结束时间：" + sub.get(sub.size() - 1).getDay());
                });
    }

}
